package middleware

import (
	"fmt"
	"github.com/gin-gonic/gin"
	retalog "github.com/lestrrat-go/file-rotatelogs"
	"github.com/rifflock/lfshook"
	"github.com/sirupsen/logrus"
	"github.com/spf13/viper"
	"math"
	"os"
	"time"
)

func Logger() gin.HandlerFunc {

	logger := logrus.New()

	filePath := "logs/log"
	// 权限
	src, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0755)
	if err != nil {
		fmt.Println("err: ", err)
	}

	logger.Out = src

	logger.SetLevel(logrus.DebugLevel)

	// 根据日期新建文件名
	logWriter, _ := retalog.New(
		filePath+"%Y%m%d.log",
		retalog.WithMaxAge(viper.GetDuration("log.deleteTime")*24*time.Hour),
		retalog.WithRotationTime(24*time.Hour),
		//retalog.WithLinkName(linkName),
	)

	writeMap := lfshook.WriterMap{
		logrus.InfoLevel:  logWriter,
		logrus.FatalLevel: logWriter,
		logrus.DebugLevel: logWriter,
		logrus.WarnLevel:  logWriter,
		logrus.ErrorLevel: logWriter,
		logrus.PanicLevel: logWriter,
	}

	// 格式化时间
	Hook := lfshook.NewHook(writeMap, &logrus.TextFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
	})

	logger.AddHook(Hook)

	return func(c *gin.Context) {
		// 开始时间
		starTime := time.Now()
		c.Next()
		stopTime := time.Since(starTime)
		NanTime := int(math.Ceil(float64(stopTime.Nanoseconds() / 1000000.0)))
		spendTime := fmt.Sprintf("%b ms", NanTime)
		// 获取请求地址
		hostName, err := os.Hostname()
		if err != nil {
			hostName = "unknown"
		}
		// 获取请求状态码
		statusCode := c.Writer.Status()
		// 获取Ip
		clientIp := c.ClientIP()
		// 获取客户端信息
		userAgent := c.Request.UserAgent()
		// 文件长度
		dataSize := c.Writer.Size()
		if dataSize < 0 {
			dataSize = 0
		}
		// 请求方法
		method := c.Request.Method
		// 路径
		path := c.Request.RequestURI

		entry := logger.WithFields(logrus.Fields{
			"HostName":   hostName,
			"StatusCode": statusCode,
			"SpendTime":  spendTime,
			"ClientIp":   clientIp,
			"Method":     method,
			"Path":       path,
			"DataSize":   dataSize,
			"Agent":      userAgent,
		})
		// 内部错误
		if len(c.Errors) > 0 {
			entry.Error(c.Errors.ByType(gin.ErrorTypePrivate).String())
		}
		if statusCode >= 500 {
			entry.Error()
		} else if statusCode >= 400 {
			entry.Warn()
		} else {
			entry.Info()
		}
	}
}
